Skip to content
View Article Network

A Brief Introduction to Git Commit Conventions

Most Git commit conventions on the internet today originate from the Angular team's format, which has evolved into many versions over time. Although this information is widely available, I decided to write an article to document it to prevent it from being lost—after all, the articles I read five years ago are no longer accessible. The current "[Angular Commit Format]" is as follows: (https://github.com/angular/angular/blob/main/CONTRIBUTING.md).

Commit Format

The Angular Commit Format is divided into three parts: header, body, and footer, separated by blank lines. The header is mandatory, the body depends on the header's type (mandatory if the type is docs), and the footer is optional.

xml
<header>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

The header format is as follows (excerpted from the Angular Commit Format):

xml
<type>(<scope>): <short summary>
  │       │             │
  │       │             └─⫸ Summary in present tense. Not capitalized. No period at the end.
  │       │
  │       └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
  │                          elements|forms|http|language-service|localize|platform-browser|
  │                          platform-browser-dynamic|platform-server|router|service-worker|
  │                          upgrade|zone.js|packaging|changelog|docs-infra|migrations|
  │                          devtools

  └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test

Type

The Type is used to categorize commits. Categories have varied over time; the current table is summarized below. The main differences are that style and chore have been removed, and CI/CD has been separated from build:

TypeDescriptionNew Description
featNew featureNew feature
fixBug fixBug fix
docsDocumentation only changesDocumentation only changes
styleChanges that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc.)
refactorA code change that neither fixes a bug nor adds a featureA code change that neither fixes a bug nor adds a feature
testAdding missing tests or correcting existing testsAdding missing tests or correcting existing tests
perfA code change that improves performanceA code change that improves performance
buildChanges that affect the build system, CI configuration, or external dependencies (example scopes: gulp, broccoli, npm)Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
choreOther changes that don't modify src or test files
ciChanges to CI configuration files and scripts (example: CircleCI, SauceLabs)

Scope

The Scope indicates the name of the affected npm package. This is defined specifically for Angular and may not apply to other programming languages. For me, in most cases, I choose to omit it or label it with the name of the project being changed.

Short Summary (Subject)

The Short Summary is a concise description of the changes. The Angular convention is as follows:

  • use the imperative, present tense: "change" not "changed" nor "changes"
  • don't capitalize the first letter
  • no dot (.) at the end

However, since I write in Chinese, I simply omit the period at the end of the sentence.

Body

The original text from the Angular team is as follows:

Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".

Explain the motivation for the change in the commit message body. This commit message should explain why you are making the change. You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.

Since I write in Chinese, there are no tense issues. In practice, unless the modification is complex or requires a specific explanation of the reason for the change, I omit the Body. Recently, a friend mentioned using Visual Studio's Copilot to help generate commit messages (if subscribed to Copilot); I might start using the generated messages as a base for the Body.

The Footer can contain breaking changes and deprecation information, and can also be used to reference GitHub issues, Jira tickets, and other PRs. For example:

text
BREAKING CHANGE: <breaking change summary>
<BLANK LINE>
<breaking change description + migration instructions>
<BLANK LINE>
<BLANK LINE>
Fixes #<issue number>

Or:

sql
DEPRECATED: <what is deprecated>
<BLANK LINE>
<deprecation description + recommended update path>
<BLANK LINE>
<BLANK LINE>
Closes #<pr number>

BREAKING CHANGE is used for major incompatible changes, and DEPRECATED is used to describe deprecated content.

In most cases, the Footer is only used to associate requirement ticket numbers, and the specific association method depends on the Issue Tracker used.

For example, in GitHub, you can use the following keywords to associate and close a PR with an issue. See the GitHub documentation "Linking a pull request to an issue".

  • close
  • closes
  • closed
  • fix
  • fixes
  • fixed
  • resolve
  • resolves
  • resolved

In GitLab, you can use the following methods to link, replacing 123 with the corresponding ID:

  • Associate Issue: #123
  • Associate MR: !123
  • Associate Snippet: $123

GitLab can also use Closes #123 or Fixes #123 to close the corresponding issue when merging the branch. See the GitLab documentation "Tutorial: It's all connected in GitLab".

Personally, since a PR or MR might not be associated with an Issue Tracker, or I might want to control the association manually, I use the prefix issue so that it is semantically clear that #123 is associated with an issue.

TIP

Keywords like close are case-insensitive. The examples in the documentation use uppercase because they appear at the beginning of a sentence; if you look closely at those appearing in the middle of a sentence, they are lowercase.

Commit Template

The above is a brief introduction to current mainstream commit conventions, but in actual operation, you might forget some less frequently used content. For example, I often forget some of the less common Types. Git supports a default Commit Template, which can help us standardize the commit message format.

Configuration Method

First, create a file named .gitmessage.txt. The filename cannot be changed. The content is as follows and can be adjusted according to your needs:

git
<type>(<scope>): <subject>

# -- Type --
# Must be one of the following:
#
# feat: New feature
# fix:  Bug fix
# docs: Documentation only changes
# style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc.)
# refactor: A code change that neither fixes a bug nor adds a feature
# perf: A code change that improves performance
# test: Adding missing tests or correcting existing tests
# build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
# chore: Other changes that don't modify src or test files
# ci: Changes to CI configuration files and scripts (example: CircleCI, SauceLabs)
#

# -- Scope --
# The scope can be anything specifying the location of the commit changes, e.g., init, runner, watcher, config, web-server, proxy, etc.
#

# -- Subject --
# The subject contains a concise description of the changes:
# Use the imperative, present tense: "change" not "changed" nor "changes"
# Do not capitalize the first letter
# Do not use a period at the end
#

# -- Body --
# Just as in the subject, use the imperative, present tense: "change" not "changed" nor "changes".
# The body should contain the motivation for the change and compare it with previous behavior.
#

# -- Footer --
# The footer should contain information about Breaking Changes and is also a reference for GitHub issue closing.
# Breaking Changes should start with the word "BREAKING CHANGE": followed by a space or two newlines. Then use the rest of the commit message.
# Deprecated should start with the word "DEPRECATED": followed by a space or two newlines. Then use the rest of the commit message.

Next, open Git Bash and enter the following commands (where ~/ defaults to C:\Users\{Windows Account}, or replace ~/ with the full path where you want to place the file):

git
git config --global commit.template ~/.gitmessage.txt
git config --global commit.cleanup strip

Explanation:

  • git config: This is the Git configuration command used to view and set Git configuration options.
  • --global: This flag indicates that the setting will be used globally, i.e., for all Git repositories. If this flag is not used, the setting will only take effect in the current Git repository.
  • commit.template: Used to specify the location of the commit message template file.
  • commit.cleanup: Used to specify how to handle commit messages during a commit. The default is whitespace, which does not ignore comment lines.
  • strip: This is the value for the commit.cleanup option, which means that comment lines and extra blank lines in the commit message are removed during the commit.

After execution, the following content will be added to the global .gitconfig. The global .gitconfig is stored in the user account folder by default on Windows, e.g., C:\Users\{Windows Account}.

text
[commit]
 cleanup = strip
 template = {Specified Path}/.gitmessage.txt

If the command is entered without --global, this content will be generated in the ./.git/config file of that repository.

TIP

If template is set to ./.gitmessage.txt, Git will use the .gitmessage.txt file in the root directory of the repository as the Commit Template.

Git has three configuration levels:

  • System configuration:
    • Location: C:\Program Files\Git\etc\gitconfig.
    • Lowest priority. This setting affects all users and projects on the entire system. It is usually a global setting configured by the system administrator, suitable for system-wide Git behavior conventions.
  • Global configuration:
    • C:\Users\{Windows Account}\.gitconfig:
    • Second highest priority. This setting applies to a single user but is overridden by Local settings. It is commonly used to set personal Git options and affects all repositories unless overridden by Local settings.
  • Local configuration:
    • .git\config
    • Highest priority. This setting is only valid for the current project and overrides Global and System settings. Since its scope is limited to a specific repository, it is particularly useful for cases where special settings are needed for a project.

TIP

The three filenames are all different XD

Actual Usage

When you enter git commit in Git Bash, the message hint: Waiting for your editor to close the file... will appear, and a text editor will open containing the content of .gitmessage.txt along with the following information:

git
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main
# Your branch is up to date with 'origin/main'.
#
# Changes to be committed:
# modified:   "{Modified Filename}"

The blank line added at the end of .gitmessage.txt is to separate it from this section of information. This section mainly explains that you should enter a commit message, that lines starting with # will be ignored, and provides a list of modified files.

Once you have modified the file and saved it, the content of the file will be used as the commit message.

If you use other Git version control software, it may not necessarily ignore lines starting with #, so you need to set commit.cleanup strip.

Currently, the version control software I know that supports Commit Template is as follows:

  • GitKraken: Click File => Preferences... => Commit on the repository tab to display the Commit Template settings. If commit.cleanup strip is not set, remember to check "Removes comments from commit messages" to ignore lines starting with #.

  • Tortoisegit: When commit.cleanup is whitespace, commits do not ignore lines starting with #.

  • Git Extensions: When commit.cleanup is whitespace, commits still ignore lines starting with # (thanks to my colleague for testing).

  • Sourcetree:

    • Mac version 4.2.8 supports it (thanks to my colleague for testing); Windows version 3.4.20 and later support it.
    • When commit.cleanup is whitespace, commits still ignore lines starting with #.

TIP

Sourcetree version 3.4.18 did not support Git Template, but I saw that on 2024/6/27, the official team closed the Jira ticket that everyone had been calling for for years, stating that it was resolved in Commit template message, so it might be supported in a later version.

Sourcetree 3.4.20 has added support. See Sourcetree release notes.

SourceTree 3.4.20 [17 September 2024]

  • Changes: Supporting git commit template feature
  • Changes: Upgrade to Git 2.46.0 and Git LFS to 3.5.1
  • Fixed: 'Push changes immediately' checkbox is disabled in No Staging View
  • Fixed: Arbitrary code execution vulnerability
  • Fixed: Interactive rebase always aborting when a merge is necessary
  • Fixed: Silent crash when creating a hotfix
  • Fixed: Sourcetree diff treats large .sql files as binary
  • Fixed: Windows Line breaks are replaced with Unix breaks on "Discard Hunk" click

Reverting Settings

You can use the following command to remove the Git Template setting:

git
git config --unset --global commit.template

Use the following command to restore the setting to ignore comment lines:

git
git config --global commit.cleanup whitespace

Change Log

  • 2024-07-23 Initial document creation.
  • 2024-09-20
    • Updated to reflect that Windows Sourcetree 3.4.20 supports the Git Commit Template feature.
    • Corrected the explanation of configuration file locations.